package com.zm.demo.core;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import static java.lang.System.err;
import static java.lang.System.out;

/**
 * @author zoum
 * @create 2020/3/9 16:58
 */
public class RateLimiter{

    public volatile List<AtomicInteger> linkedList = new LinkedList<>();
    public ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
    public ExecutorService executorService = Executors.newFixedThreadPool(150);

    private int max;
    private int size;
    private AtomicInteger count = new AtomicInteger(0);
    private volatile Boolean isLock = Boolean.FALSE;

    RateLimiter(int max, int second){
        this.max = max;
        this.size=second;
        for (int i = 0; i < size; i++) {
            linkedList.add(new AtomicInteger(0));
        }

        //每个rateLimiter初始化一个定时任务，每秒执行一次，移除第一个结点
        scheduledExecutorService.scheduleWithFixedDelay(()->{
            out.println(linkedList.toString());
            if (linkedList.size()>0){
                isLock = Boolean.TRUE;
                //移除第一个结点
                AtomicInteger head =  linkedList.remove(0);
                //统计数减去第一个结点的数
                count.addAndGet(-head.get());
                //头结点初始化为0
                head.set(0);
                //将头节点加到列表尾部，避免不断的创建新对象导致GC
                linkedList.add(head);
                isLock = Boolean.FALSE;
            }
        }, 1, 1, TimeUnit.SECONDS);

    }

    public void add(){
        while (true){
            if (!isLock){
                this.linkedList.get(this.size-1).getAndIncrement();
                break;
            }
        }
        if (this.count.incrementAndGet() > this.max){
            err.println("request limited!!!" + this.linkedList.toString());
            throw new RuntimeException("");
        }else {
            out.println("pass.");
        }
    }

    public static void main(String[]args) throws Exception{
        RateLimiter rateLimiter = new RateLimiter(100, 5);

        while (true){
            for (int i = 0; i < 100; i++) {
                Future<Boolean> result = rateLimiter.executorService.submit(()->{
                    try {
                        rateLimiter.add();
                        return true;
                    } catch (Exception e){
                        e.printStackTrace();
                        return false;
                    }
                });

                if (!result.get()){
                    Thread.sleep(1000L);
                    break;
                }
            }

        }

//        rateLimiter.executorService.shutdown();
    }
}
